﻿
Imports std

Public Class defineDefination
    Public Const name As String = "define"
    Public Const separator As Char = character.blank
    Public Const defineSeparator As String = character.sheffer + character.equalSign
    Public Const selectStart As Char = character.leftBrace
    Public Const selectEnd As Char = character.rightBrace

    Private m As vector(Of pair(Of String, String)) = Nothing

    Private Sub insertDefination(ByVal line As String)
        If strseg(line, Nothing, line, separator) Then
            Dim name As String = Nothing
            Dim value As String = Nothing
            If strseg(line, name, value, defineSeparator) Then
                m.push_back(make_pair(name, value))
            Else
                raiseError("do not have a defineSeparator [" + defineSeparator + "], invalid defination, ignore.", _
                            errorHandle.errorType.user)
            End If
        Else
            raiseError("do not have a separator [" + separator + "], invalid defination, ignore.", _
                       errorHandle.errorType.user)
        End If
    End Sub

    'read all definations, and remove them from script
    Private Sub readDefination(ByVal script As String, ByRef output As String)
        output = Nothing
        Dim index As Int64
        index = 0
        Dim lastindex As Int64
        Dim len As Int64
        len = strlen(script)
        Dim line As String = Nothing
        While index < len
            lastindex = index
            If readPredictLine(script, name, index, line) Then
                insertDefination(line)
            Else
                output += strmid(script, lastindex, index - lastindex)
            End If
        End While
    End Sub

    Private Class definefirst
        Public Enum type
            str
            match

            invalid
        End Enum

        Public t As type = type.invalid
        Public i As Int64 = 0
        Public s As String = Nothing
    End Class

    Private Shared Sub generateStrDefineFirst(ByVal define As String, _
                                              ByVal last As Int64, ByVal i As Int64, _
                                              ByVal output As vector(Of definefirst))
        assert(Not output Is Nothing, "output is nothing.")
        Dim s As String = Nothing
        If last < i Then
            s = strmid(define, last, i - last)
            kickLeftRightBlank(s)
        End If

        If Not isemptystring(s) Then
            Dim f As definefirst = Nothing
            f = New definefirst()
            f.t = definefirst.type.str
            f.s = s
            output.push_back(f)
        End If
    End Sub

    Private Shared Sub separateDefine(ByVal define As String, ByRef output As vector(Of definefirst))
        If output Is Nothing Then
            output = New vector(Of definefirst)()
        Else
            output.clear()
        End If

        Dim i As Int64
        Dim last As Int64
        last = 0
        For i = 0 To strlen(define) - 1
            If strsame(define, i, selectStart, 0, strlen(selectStart)) Then
                generateStrDefineFirst(define, last, i, output)
                last = i
                i = strindexof(define, selectEnd, i, 1)
                If i <> npos Then
                    Dim matchIndex As Int64
                    If convertor.toint64(strmid(define, last + 1, i - last - 1), matchIndex) Then
                        Dim f As definefirst = Nothing
                        f = New definefirst()
                        f.t = definefirst.type.match
                        f.i = matchIndex
                        output.push_back(f)
                    Else
                        i = last
                    End If
                Else
                    i = last
                End If
            End If
        Next
        generateStrDefineFirst(define, last, i, output)
    End Sub

    Private Shared Sub replaceDefine(ByVal script As String, ByRef output As String, ByVal m As pair(Of String, String))
        Dim defines As vector(Of definefirst) = Nothing
        separateDefine(m.first, defines)
        If defines.size() > 1 AndAlso defines(0).t = definefirst.type.str Then
            Dim match As map(Of Int64, String) = Nothing
            Dim i As Int64
            i = 0
            While i < strlen(script)
                If strsame(script, i, defines(0).s, 0, strlen(defines(0).s)) Then

                Else
                    i += 1
                End If
            End While
        Else
            If defines.size() = 0 Then
                raiseError("cannot handle define without left expression, ignore.", _
                           errorHandle.errorType.user)
            Else
                If defines(0).t <> definefirst.type.str Then
                    raiseError("cannot handle define which starts from a match {x}, treat as normal string replace.", _
                                errorHandle.errorType.user)
                End If
                'normal #define a!=b, use string.replace directly
                output = strrplc(script, m.first, m.second)
            End If
        End If
    End Sub

    Private Sub replaceDefines(ByVal script As String, ByRef output As String)
        output = Nothing
        Dim i As Int64
        For i = 0 To m.size() - 1
            replaceDefine(script, output, m(i))
            copy(script, output)
        Next
    End Sub

    Public Sub define(ByVal script As String, ByRef output As String)
        readDefination(script, output)
        copy(script, output)
        replaceDefines(script, output)
    End Sub

    Public Sub define(ByRef script As String)
        Dim output As String = Nothing
        define(script, output)
        script = output
    End Sub

    Public Sub reset()
        If m Is Nothing Then
            m = New vector(Of pair(Of String, String))()
        Else
            m.clear()
        End If
    End Sub

    Public Sub New()
        reset()
    End Sub
End Class

Public Module _define
    Private Class definePool
        Inherits pool(Of defineDefination)

        Protected Overrides Function create() As defineDefination
            Return New defineDefination()
        End Function
    End Class

    Private p As definePool = Nothing

    Sub New()
        p = New definePool()
    End Sub

    Public Sub define(ByRef script As String)
        Dim inst As defineDefination = Nothing
        Dim index As Int64 = npos
        If p.getfree(index, inst) Then
            inst.define(script)
            inst.reset()
            p.release(index)
        Else
            raiseError("cannot get free predict inst, ignore predict.", errorHandle.errorType.exclamation)
        End If
    End Sub
End Module
